/*
 * Copyright (c) 2020 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "camera_kit.h"
// #include "recorder.h"
#include "audio_capturer.h"

#include <algorithm>
#include <cstring>
#include <fcntl.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

using namespace std;
using namespace OHOS;
using namespace OHOS::Media;
using namespace OHOS::Audio;

static int32_t SampleGetRecordFd()
{
    struct timeval tv = {};
    gettimeofday(&tv, nullptr);
    struct tm *ltm = localtime(&tv.tv_sec);
    int32_t fd = -1;
    if (ltm != nullptr) {
        ostringstream ss("Capture_");
        ss << "Record" << ltm->tm_hour << "-" << ltm->tm_min << "-" << ltm->tm_sec << ".mp4";
        fd = open(("/sdcard/" + ss.str()).c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
        cout << "Open "
             << "/sdcard/" << ss.str() << endl;

        if (fd == -1) {
            cout << "Open recorder file failed. strerr=" << strerror(errno) << endl;
        }
    }
    return fd;
}

static void SampleSaveCapture(const char *p, uint32_t size)
{
    cout << "Start saving picture" << endl;
    struct timeval tv = {};
    gettimeofday(&tv, nullptr);
    struct tm *ltm = localtime(&tv.tv_sec);
    if (ltm != nullptr) {
        ostringstream ss("Capture_");
        ss << "Capture" << ltm->tm_hour << "-" << ltm->tm_min << "-" << ltm->tm_sec << ".jpg";

        ofstream pic("/sdcard/" + ss.str(), ofstream::out | ofstream::trunc);
        cout << "write " << size << " bytes" << endl;
        pic.write(p, size);
        pic.close();
        cout << "Saving picture end" << endl;
    }
}

// Recorder *SampleCreateRecorder()
// {
//     int ret = 0;
//     int32_t sampleRate = 48000;
//     int32_t channelCount = 1;
//     AudioCodecFormat audioFormat = AAC_LC;
//     AudioSourceType inputSource = AUDIO_MIC;
//     int32_t audioEncodingBitRate = sampleRate;
//     VideoSourceType source = VIDEO_SOURCE_SURFACE_ES;
//     int32_t frameRate = 30;
//     double fps = 30;
//     int32_t rate = 4096;
//     int32_t sourceId = 0;
//     int32_t audioSourceId = 0;
//     int32_t width = 1920;
//     int32_t height = 1080;
//     VideoCodecFormat encoder;
//     encoder = HEVC;
//     width = 1920;
//     height = 1080;
//     Recorder *recorder = new Recorder();
//     // if ((ret = recorder->SetVideoSource(source, sourceId)) != SUCCESS) {
//     //     cout << "SetVideoSource failed." << ret << endl;
//     //     goto ERROR;
//     // }
//     // if ((ret = recorder->SetVideoEncoder(sourceId, encoder)) != SUCCESS) {
//     //     cout << "SetVideoEncoder failed." << ret << endl;
//     //     goto ERROR;
//     // }
//     // if ((ret = recorder->SetVideoSize(sourceId, width, height)) != SUCCESS) {
//     //     cout << "SetVideoSize failed." << ret << endl;
//     //     goto ERROR;
//     // }
//     // if ((ret = recorder->SetVideoFrameRate(sourceId, frameRate)) != SUCCESS) {
//     //     cout << "SetVideoFrameRate failed." << ret << endl;
//     //     goto ERROR;
//     // }
//     // if ((ret = recorder->SetVideoEncodingBitRate(sourceId, rate)) != SUCCESS) {
//     //     cout << "SetVideoEncodingBitRate failed." << ret << endl;
//     //     goto ERROR;
//     // }
//     // if ((ret = recorder->SetCaptureRate(sourceId, fps)) != SUCCESS) {
//     //     cout << "SetCaptureRate failed." << ret << endl;
//     //     goto ERROR;
//     // }
//     if ((ret = recorder->SetAudioSource(inputSource, audioSourceId)) != SUCCESS) {
//         cout << "SetAudioSource failed." << ret << endl;
//         goto ERROR;
//     }
//     if ((ret = recorder->SetAudioEncoder(audioSourceId, audioFormat)) != SUCCESS) {
//         cout << "SetAudioEncoder failed." << ret << endl;
//         goto ERROR;
//     }
//     if ((ret = recorder->SetAudioSampleRate(audioSourceId, sampleRate)) != SUCCESS) {
//         cout << "SetAudioSampleRate failed." << ret << endl;
//         goto ERROR;
//     }
//     if ((ret = recorder->SetAudioChannels(audioSourceId, channelCount)) != SUCCESS) {
//         cout << "SetAudioChannels failed." << ret << endl;
//         goto ERROR;
//     }
//     if ((ret = recorder->SetAudioEncodingBitRate(audioSourceId, audioEncodingBitRate)) != SUCCESS) {
//         cout << "SetAudioEncodingBitRate failed." << ret << endl;
//         goto ERROR;
//     }
//     if ((ret = recorder->SetMaxDuration(36000)) != SUCCESS) { // 36000s=10h
//         cout << "SetAudioEncodingBitRate failed." << ret << endl;
//         goto ERROR;
//     }
//     return recorder;

// ERROR:
//     delete recorder;
//     return nullptr;
// }

static void write_video(uint8_t *d, int len)
{
    static int fd = 0;

    if (d == NULL || len == 0)
    {
        if (fd > 0)
        {
            close(fd);
            printf("=====>> close %d \n", fd);
        }
        fd = 0;
    }
    else
    {
        if (fd < 1)
        {
            fd = open("/sdcard/tmp.vdo", O_RDWR | O_CREAT | O_TRUNC);
            printf("=====>> open %d \n", fd);
        }
        if (fd > 0)
            write(fd, d, len);
    }
}

class SampleFrameStateCallback : public FrameStateCallback {
public:
    void OnFrameFinished(Camera &camera, FrameConfig &fc, FrameResult &result) override
    {
        static int frameCount = 0;
    
        if (fc.GetFrameConfigType() == FRAME_CONFIG_CAPTURE) {

            list<Surface *> surfaceList = fc.GetSurfaces();
            for (Surface *surface : surfaceList) {
                SurfaceBuffer *buffer = surface->AcquireBuffer();
                if (buffer != nullptr) {
                    uint8_t *virtAddr = static_cast<uint8_t *>(buffer->GetVirAddr());

                    printf("---->> frame %d // size %d // %02X %02X %02X %02X %02X %02X \n",
                        frameCount++, buffer->GetSize(),
                        virtAddr[0], virtAddr[1], virtAddr[2], virtAddr[3], virtAddr[4], virtAddr[5]);

                    // if (virtAddr != nullptr)
                    //     SampleSaveCapture(virtAddr, buffer->GetSize());
                
                    surface->ReleaseBuffer(buffer);
                }
                
                delete surface;
            }
        }
        else
            cout << "===== Receive frame complete inform. ===== " << fc.GetFrameConfigType() << endl;

        write_video(NULL, 0);

        delete &fc;
    }

    Camera *cam_ = nullptr;
};

class TestIBufferConsumerListener : public IBufferConsumerListener {
public:
    TestIBufferConsumerListener(FrameConfig *f) : fc(f) {}
    void OnBufferAvailable() override
    {
        printf("----- OnBufferAvailable ----- \n");

        list<Surface *> surfaceList = fc->GetSurfaces();
        for (Surface *surface : surfaceList)
        {
            SurfaceBuffer *buffer = surface->AcquireBuffer();
            if (buffer != nullptr)
            {
                uint8_t *virtAddr = static_cast<uint8_t *>(buffer->GetVirAddr());

                printf("---->> frame %d // size %d // %02X %02X %02X %02X %02X %02X \n",
                    frameCount++, buffer->GetSize(),
                    virtAddr[0], virtAddr[1], virtAddr[2], virtAddr[3], virtAddr[4], virtAddr[5]);

                write_video(virtAddr, buffer->GetSize());

                // if (virtAddr != nullptr)
                //     SampleSaveCapture(virtAddr, buffer->GetSize());

                surface->ReleaseBuffer(buffer);
            }

            // delete surface;
        }
    }

    FrameConfig *fc;
    int frameCount = 0;
};

class SampleCameraStateMng : public CameraStateCallback {
public:
    SampleCameraStateMng() = delete;
    SampleCameraStateMng(EventHandler &eventHdlr) : eventHdlr_(eventHdlr) {}
    ~SampleCameraStateMng() { CloseRecorder(); }
    void OnCreated(Camera &c) override
    {
        cout << "-- OnCreated --" << endl;
        auto config = CameraConfig::CreateCameraConfig();
        config->SetFrameStateCallback(&fsCb_, &eventHdlr_);
        c.Configure(*config);
        cam_ = &c;
        fsCb_.cam_ = cam_;
        listener = new TestIBufferConsumerListener(nullptr);
    }
    void OnCreateFailed(const std::string cameraId, int32_t errorCode) override {}
    void OnReleased(Camera &c) override {}

    void CloseRecorder()
    {
        // if (recorder_ != nullptr) {
        //     recorder_->Stop(true);
        //     recorder_->Release();
        //     delete recorder_;
        //     recorder_ = nullptr;
        // }
        // if (recordFd_ != -1) {
        //     close(recordFd_);
        //     recordFd_ = -1;
        // }
    }

    int PrepareRecorder()
    {
        // if (cam_ == nullptr) {
        //     cout << "Camera is not ready." << endl;
        //     return -1;
        // }
        // if (recorder_ == nullptr) {
        //     recorder_ = SampleCreateRecorder();
        // }
        // if (recorder_ == nullptr) {
        //     cout << "Recorder not available." << endl;
        //     return -1;
        // }
        // if (recordFd_ == -1) {
        //     recordFd_ = SampleGetRecordFd();
        // }
        // if (recordFd_ == -1) {
        //     cout << "Create fd failed." << endl;
        //     return -1;
        // }
        return SUCCESS;
    }

    void StartRecord()
    {
        int ret;
        // if (recordState_ == STATE_RUNNING) {
        //     cout << "Camera is already recording." << endl;
        //     return;
        // }
        // ret = PrepareRecorder();
        // if (ret != SUCCESS) {
        //     cout << "PrepareRecorder failed." << endl;
        //     CloseRecorder();
        //     return;
        // }
        // ret = recorder_->SetOutputFile(recordFd_);
        // if (ret != SUCCESS) {
        //     cout << "SetOutputPath failed. ret=" << ret << endl;
        //     CloseRecorder();
        //     return;
        // }
        // ret = recorder_->Prepare();
        // if (ret != SUCCESS) {
        //     cout << "Prepare failed. ret=" << ret << endl;
        //     CloseRecorder();
        //     return;
        // }
        // ret = recorder_->Start();
        // if (ret != SUCCESS) {
        //     cout << "recorder start failed. ret=" << ret << endl;
        //     CloseRecorder();
        //     return;
        // }
        // Surface *surface = (recorder_->GetSurface(0)).get();

        Surface *surface = Surface::CreateSurface();
    
        FrameConfig *fc = new FrameConfig(FRAME_CONFIG_RECORD);
        listener->fc = fc;
        surface->RegisterConsumerListener(*listener);

        surface->SetWidthAndHeight(1920, 1080);
        surface->SetQueueSize(3);
        surface->SetSize(1024 * 1024);

        fc->AddSurface(*surface);
        ret = cam_->TriggerLoopingCapture(*fc);
        if (ret != 0) {
            delete fc;
            CloseRecorder();
            cout << "camera start recording failed. ret=" << ret << endl;
            return;
        }
        recordState_ = STATE_RUNNING;
        cout << "camera start recording succeed." << endl;
    }

    void StartPreview()
    {
        if (cam_ == nullptr) {
            cout << "Camera is not ready." << endl;
            return;
        }
        if (previewState_ == STATE_RUNNING) {
            cout << "Camera is already previewing." << endl;
            return;
        }

        Surface *surface = Surface::CreateSurface();
        if (surface == nullptr) {
            cout << "CreateSurface failed" << endl;
            return;
        }
    
        FrameConfig *fc = new FrameConfig(FRAME_CONFIG_PREVIEW);

        surface->SetWidthAndHeight(1920, 1080); /* 1920:width,1080:height */
        surface->SetUserData("region_position_x", "0");
        surface->SetUserData("region_position_y", "0");
        surface->SetUserData("region_width", "800");
        surface->SetUserData("region_height", "480");

        fc->AddSurface(*surface);
        int32_t ret = cam_->TriggerLoopingCapture(*fc);
        if (ret != 0) {
            delete fc;
            cout << "camera start preview failed. ret=" << ret << endl;
            return;
        }
        
        delete surface;
        previewState_ = STATE_RUNNING;
        cout << "camera start preview succeed." << endl;
    }

    void Capture()
    {
        if (cam_ == nullptr) {
            cout << "Camera is not ready." << endl;
            return;
        }
        FrameConfig *fc = new FrameConfig(FRAME_CONFIG_CAPTURE);
        Surface *surface = Surface::CreateSurface();
        if (surface == nullptr) {
            delete fc;
            cout << "CreateSurface failed" << endl;
            return;
        }
        surface->SetWidthAndHeight(1920, 1080); /* 1920:width,1080:height */
        fc->AddSurface(*surface);
        cam_->TriggerSingleCapture(*fc);
    }

    void Stop()
    {
        if (cam_ == nullptr) {
            cout << "Camera is not ready." << endl;
            return;
        }
        cam_->StopLoopingCapture();
        if (recordState_ == STATE_RUNNING) {
            CloseRecorder();
        }
        recordState_ = STATE_IDLE;
        previewState_ = STATE_IDLE;
    }

private:
    enum State : int32_t { STATE_IDLE, STATE_RUNNING, STATE_BUTT };
    State previewState_ = STATE_IDLE;
    State recordState_ = STATE_IDLE;
    EventHandler &eventHdlr_;
    Camera *cam_ = nullptr;
    // int32_t recordFd_ = -1;
    // Recorder *recorder_ = nullptr;
    SampleFrameStateCallback fsCb_;
    TestIBufferConsumerListener *listener = nullptr;
};

class SampleCameraDeviceCallback : public CameraDeviceCallback {
};

void SampleHelp()
{
    cout << "*******************************************" << endl;
    cout << "Select the behavior of avrecorder." << endl;
    cout << "1: Capture" << endl;
    cout << "2: Record(Press s to stop)" << endl;
    cout << "3: Preview(Press s to stop)" << endl;
    cout << "q: quit the sample." << endl;
    cout << "*******************************************" << endl;
}

int audio_test()
{
    int ret;

    int fd = open("/sdcard/tmp.aac", O_RDWR | O_CREAT | O_TRUNC);
    printf("----->> open(%d)\n", fd);

    AudioCapturer *ac = new AudioCapturer();

    AudioCapturerInfo info;
    info.inputSource = AUDIO_MIC;
    info.audioFormat = AAC_LC;
    info.sampleRate = 16000;
    info.channelCount = 1;
    info.bitRate = 32000;
    info.streamType = TYPE_MEDIA;
    info.bitWidth = BIT_WIDTH_16;

    ret = ac->SetCapturerInfo(info);
    printf("----->> SetCapturerInfo(%d)\n", ret);

    printf("----->> Start(%d)\n", ac->Start() ? 0 : (-1));
    
    int frameSize = ac->GetFrameCount() * 2;
    printf("----->> GetFrameCount(%d)\n", frameSize);
    if (frameSize < 1)
    {
        if (fd > 0)
            close(fd);
        delete ac;
        return -1;
    }

    uint8_t buff[frameSize];
    int timeout = 0;

    while(timeout++ < 200)
    {
        ret = ac->Read(buff, frameSize, false);
        if (ret > 0)
        {
            printf("----->> Read: %d -- %2X %2X %2X %2X \n",
                ret,
                buff[0],
                buff[1],
                buff[2],
                buff[3]);
            
            if (fd > 0)
                write(fd, buff, ret);
        }
        else
            usleep(1000);
    }

    printf("----->> Stop(%d)\n", ac->Stop() ? 0 : (-1));
    printf("----->> Release(%d)\n", ac->Release() ? 0 : (-1));

    if (fd > 0)
        close(fd);
    delete ac;

    return 0;
}


int main()
{
    cout << "Camera sample begin." << endl;
    SampleHelp();
    CameraKit *camKit = CameraKit::GetInstance();
    if (camKit == nullptr) {
        cout << "Can not get CameraKit instance" << endl;
        return 0;
    }
    list<string> camList = camKit->GetCameraIds();
    string camId;
    for (auto &cam : camList) {
        cout << "camera name:" << cam << endl;
        const CameraAbility *ability = camKit->GetCameraAbility(cam);
        /* find camera which fits user's ability */
        list<CameraPicSize> sizeList = ability->GetSupportedSizes(0);
        for (auto &pic : sizeList) {
            if (pic.width == 1920 && pic.height == 1080) {
                camId = cam;
                break;
            }
        }
    }

    if (camId.empty()) {
        cout << "No available camera.(1080p wanted)" << endl;
        return 0;
    }

    EventHandler eventHdlr; // Create a thread to handle callback events
    SampleCameraStateMng CamStateMng(eventHdlr);

    camKit->CreateCamera(camId, CamStateMng, eventHdlr);

    char input;
    while (cin >> input) {
        switch (input) {
            case '1':
                CamStateMng.Capture();
                break;
            case '2':
                CamStateMng.StartRecord();
                break;
            case '3':
                CamStateMng.StartPreview();
                break;
            case '4':
                audio_test();
                break;
            case 's':
                CamStateMng.Stop();
                break;
            case 'q':
                CamStateMng.Stop();
                goto EXIT;
            default:
                SampleHelp();
                break;
        }
    }
EXIT:
    write_video(NULL, 0);
    cout << "Camera sample end." << endl;
    return 0;
}
